|
1
|
|
|
/** findMdpInsert and findCode functions use a similar layout to return the location and contents |
|
2
|
|
|
* .start => points at the character in the string why the other item starts (ie. comment or code block) |
|
3
|
|
|
* .length => is the overall length of the comment or code block. |
|
4
|
|
|
* .internalStart => points at the character in the string where the internal payload starts |
|
5
|
|
|
* .internalLength => is the length of the internal payload |
|
6
|
|
|
* .commandString => is the command string found within the particular item |
|
7
|
|
|
* .info => is a structure containing further info about what was found |
|
8
|
|
|
* if start is returned as -1 then nothing was found |
|
9
|
|
|
* |
|
10
|
|
|
* The internalStart/internaLength defines the internal content which will be replaced. This does not include |
|
11
|
|
|
* leading and lagging CRLF/LF. So the replacement text is not required to have either leading or lagging line |
|
12
|
|
|
* endings. However, if the internalLength is negative this means that leading CRLF or LF must be added by the insertion |
|
13
|
|
|
* routine. The reason for this is that it allows insertions between code fences or mdpInsert pairs which have zero lines |
|
14
|
|
|
* between them. |
|
15
|
|
|
* |
|
16
|
|
|
**/ |
|
17
|
|
|
|
|
18
|
|
|
const {findCode} = require('./findCode.js') |
|
19
|
|
|
|
|
20
|
|
|
export function findMdpInsert (txt, start) { |
|
21
|
|
|
let s = _findMdpStartUnfenced(txt, start) |
|
22
|
|
|
if (s.start === -1) { return s } |
|
23
|
|
|
let s1 = JSON.parse(JSON.stringify(s)) // create copy |
|
24
|
|
|
let depth = 1 |
|
25
|
|
|
let e |
|
26
|
|
|
let posn = s1.internalStart - 2 |
|
27
|
|
|
do { |
|
28
|
|
|
e = _findMdpEndUnfenced(txt, s, posn) |
|
29
|
|
|
if (e.start === -1) { |
|
30
|
|
|
// we have not found any more ends so we need to return a fail |
|
31
|
|
|
return e |
|
32
|
|
|
} |
|
33
|
|
|
s1 = _findMdpStartUnfenced(txt, posn) |
|
34
|
|
|
if (s1.start !== -1 && s1.start < (e.internalStart + e.internalLength)) { |
|
35
|
|
|
// we have found another start pattern before the end of the end pattern - so a new level |
|
36
|
|
|
depth++ |
|
37
|
|
|
posn = s1.internalStart - 2 |
|
38
|
|
|
} else { |
|
39
|
|
|
depth-- |
|
40
|
|
|
posn = e.start + e.length |
|
41
|
|
|
} |
|
42
|
|
|
if (depth > 5) { return {start: -1} } |
|
43
|
|
|
} while (depth !== 0) |
|
44
|
|
|
return e |
|
45
|
|
|
} |
|
46
|
|
|
|
|
47
|
|
|
function _findMdpStartUnfenced (txt, start) { |
|
48
|
|
|
let lookFrom = start |
|
49
|
|
|
let m, c |
|
50
|
|
|
while (true) { |
|
51
|
|
|
m = _findMdpStart(txt, lookFrom) |
|
52
|
|
|
if (m.start === -1) { return m } |
|
53
|
|
|
// we need to find the first code which ends after the mdp start |
|
54
|
|
|
c = _findCodeEndingAfter(txt, lookFrom, m.start) |
|
55
|
|
|
if (c.start === -1 || m.start < c.start) { |
|
56
|
|
|
// the mdp start we've found is not within a code fence |
|
57
|
|
|
break |
|
58
|
|
|
} |
|
59
|
|
|
// the mdp start we've found is within a code fence so find the next one |
|
60
|
|
|
lookFrom = c.start + c.length |
|
61
|
|
|
} |
|
62
|
|
|
return m |
|
63
|
|
|
|
|
64
|
|
|
function _findMdpStart (txt, start) { |
|
65
|
|
|
let regex = /(\r\n|\n|^)([ ]{0,3}\[>[^\r\n\t\0[\]]*\]: # (\([^\r\n\t\0]*\)|"[^\r\n\t\0]*"|'[^\r\n\t\0]*'))(\r\n|\n)/g |
|
66
|
|
|
regex.lastIndex = start |
|
67
|
|
|
let regexResult = regex.exec(txt) |
|
68
|
|
|
if (regexResult === null) { return {start: -1} } |
|
69
|
|
|
let r = { |
|
70
|
|
|
start: regexResult.index + regexResult[1].length, |
|
71
|
|
|
internalStart: regexResult.index + regexResult[0].length, |
|
72
|
|
|
commandString: regexResult[3].substring(1, regexResult[3].length - 1) |
|
73
|
|
|
} |
|
74
|
|
|
return r |
|
75
|
|
|
} |
|
76
|
|
|
} |
|
77
|
|
|
|
|
78
|
|
|
function _findMdpEndUnfenced (txt, opening, start) { |
|
79
|
|
|
let lookFrom = start |
|
80
|
|
|
let m, c |
|
81
|
|
|
while (true) { |
|
82
|
|
|
m = _findMdpEnd(txt, opening, lookFrom - 2) |
|
83
|
|
|
if (m.start === -1) { return m } |
|
84
|
|
|
// we need to find the first code which ends after the mdpEnd starts |
|
85
|
|
|
c = _findCodeEndingAfter(txt, lookFrom, (m.internalStart + m.internalLength)) |
|
86
|
|
|
if (c.start === -1 || (m.internalStart + m.internalLength) < c.start) { break } // the mdp end we've found is not within a code fence |
|
87
|
|
|
// the mdp end we've found is within a code fence so find the next one |
|
88
|
|
|
lookFrom = c.start + c.length |
|
89
|
|
|
} |
|
90
|
|
|
return m |
|
91
|
|
|
|
|
92
|
|
|
function _findMdpEnd (txt, opening, start) { |
|
93
|
|
|
let r = JSON.parse(JSON.stringify(opening)) // create copy of opening structure passed in |
|
94
|
|
|
let regex = /(\r\n|\n)([ ]{0,3}\[<[^\r\n\t\0[\]]*\]: #)(\r\n|\n|$)/g |
|
95
|
|
|
regex.lastIndex = start |
|
96
|
|
|
let regexResult = regex.exec(txt) |
|
97
|
|
|
if (regexResult === null) { return {start: -1} } |
|
98
|
|
|
r.internalLength = regexResult.index - r.internalStart |
|
99
|
|
|
r.length = regexResult.index + regexResult[0].length - regexResult[3].length - r.start |
|
100
|
|
|
return r |
|
101
|
|
|
} |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
|
|
function _findCodeEndingAfter (txt, start, endingAfter) { |
|
105
|
|
|
// returns the first code block which ends after the position specified |
|
106
|
|
|
// the search starts at start |
|
107
|
|
|
let pos = start |
|
108
|
|
|
let c |
|
109
|
|
|
do { |
|
110
|
|
|
c = findCode(txt, pos) |
|
111
|
|
|
pos = c.start + c.length |
|
112
|
|
|
} while (c.start !== -1 && pos <= endingAfter) |
|
113
|
|
|
return c |
|
114
|
|
|
} |
|
115
|
|
|
|